package org.sais.exscanner;

import java.io.File;

import jwbroek.cuelib.CueParser;
import jwbroek.cuelib.CueSheet;
import jwbroek.cuelib.Position;
import jwbroek.cuelib.TrackData;

import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;

import android.app.Service;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.provider.MediaStore.Audio;
import android.util.Log;
import android.webkit.MimeTypeMap;

public class ScanService extends Service {

	public static final String ACTION_SCAN_SINGLE = "org.sais.exscanner.SCAN";
	public static final String ACTION_SCAN_DIR = "org.sais.exscanner.SCAN_DIR";

	public static final String MIME_EX_AUDIO = "ex-audio/";
	private static final String MIME_EX_AUDIO_CUE_TRACK = "ex-audio/cue_track";

	public static final String TAG = "ExtendedScanner";

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		Log.i(TAG, "Extra scanner started, intent=" + intent);
		new CleanTask().execute();
		String action = intent.getAction();
		String path = intent.getData().getPath();
		if (path == null) {
			return;
		}
		if (action.equals(ACTION_SCAN_SINGLE)) {
			new MediaScanTask().execute(path);
		} else if (action.equals(ACTION_SCAN_DIR)) {
			scanRecursively(new File(path));
			if (new File("/emmc").exists() && !path.startsWith("/emmc")) {
				scanConnectionRecursively(new File("/emmc"));
			}
		}
	}

	private void scanConnectionRecursively(File dir) {
		File[] ss = dir.listFiles();
		if (ss == null) {
			return;
		}
		for (File f : ss) {
			if (f.isDirectory()) {
				scanConnectionRecursively(f);
			} else {
				String name = f.getName();
				int lastdot = name.lastIndexOf('.');
				if (lastdot > 0) {
					String extension = name.substring(lastdot + 1);
					String mime = MimeTypeMap.getSingleton()
							.getMimeTypeFromExtension(extension);
					new MediaScannerNotifier(this, f.getPath(), mime);
				}
			}
		}
	}

	private void scanRecursively(File dir) {
		try {
			File[] files = dir.listFiles();
			if (files == null) {
				return;
			}
			for (File f : files) {
				if (f.isDirectory()) {
					scanRecursively(f);
				} else {
					String s = f.getPath();
					String s_lower = s.toLowerCase();
					if (s_lower.endsWith("flac") || s_lower.endsWith("ogg")
							|| s_lower.endsWith("mp3")
							|| s_lower.endsWith("mp4")
							|| s_lower.endsWith("m4a")
							|| s_lower.endsWith("mp4p")) {
						new MediaScanTask().execute(s);
					} else if (s_lower.endsWith("cue")) {
						new CueScanTask().execute(f);
					}
				}
			}
		} catch (Exception e) {
			Log.e(TAG, "", e);
		}
	}

	class MediaScanTask extends AsyncTask<String, Void, Void> {

		@Override
		protected Void doInBackground(String... params) {
			try {
				// Log.v(TAG, params[0]);
				AudioFile f = AudioFileIO.read(new File(params[0]));
				Tag tag = f.getTag();
				ContentValues values = new ContentValues();
				String title = tag.getFirst(FieldKey.TITLE);
				String data;
				if (Integer.parseInt(Build.VERSION.SDK) >= 8) {
					data = f.getFile().getCanonicalPath();
				} else {
					data = f.getFile().getPath();
				}
				values.put(Audio.Media.TITLE, title);
				values.put(Audio.Media.ARTIST, tag.getFirst(FieldKey.ARTIST));
				values.put(Audio.Media.ALBUM, tag.getFirst(FieldKey.ALBUM));
				values.put(Audio.Media.COMPOSER,
						tag.getFirst(FieldKey.COMPOSER));
				values.put(Audio.Media.DATE_ADDED, System.currentTimeMillis());
				values.put(Audio.Media.DATE_MODIFIED,
						System.currentTimeMillis());
				values.put(Audio.Media.DISPLAY_NAME, tag.getFirst(title));
				values.put(Audio.Media.DURATION, f.getAudioHeader()
						.getTrackLength() * 1000);
				values.put(Audio.Media.IS_MUSIC, 1);
				values.put(Audio.Media.SIZE, f.getFile().length());
				values.put(Audio.Media.TRACK, tag.getFirst(FieldKey.TRACK));
				values.put(Audio.Media.YEAR, tag.getFirst(FieldKey.YEAR));
				values.put(Audio.Media.GENRE, tag.getFirst(FieldKey.GENRE));
				values.put(Audio.Media.DATA, data);
				values.put(Audio.Media.MIME_TYPE, MIME_EX_AUDIO
						+ f.getAudioHeader().getFormat().split(" ")[0]);
				int updated = getContentResolver().update(
						Audio.Media.EXTERNAL_CONTENT_URI, values,
						Audio.Media.DATA + "=?", new String[] { data });
				// Log.i(TAG, updated + " rows updated.");
				if (updated <= 0) {
					Uri uri = getContentResolver().insert(
							Audio.Media.EXTERNAL_CONTENT_URI, values);
					// Log.i(TAG, "Newly inserted, uri=" + uri);
				}
			} catch (Exception e) {
				Log.e(TAG, "", e);
			}
			return null;
		}

	}

	class CueScanTask extends AsyncTask<File, Void, Void> {

		@Override
		protected Void doInBackground(File... params) {
			try {
				File file = params[0];
				CueSheet cue = CueParser.parse(file);
				String album = cue.getTitle();
				int index = 0;
				for (TrackData track : cue.getAllTrackData()) {
					ContentValues values = new ContentValues();
					String title = track.getTitle();
					String data;
					if (Integer.parseInt(Build.VERSION.SDK) >= 8) {
						data = file.getCanonicalPath();
					} else {
						data = file.getPath();
					}
					values.put(Audio.Media.TITLE, title);
					values.put(Audio.Media.ARTIST, track.getPerformer());
					values.put(Audio.Media.ALBUM, album);
					values.put(Audio.Media.COMPOSER, track.getSongwriter());
					values.put(Audio.Media.DATE_ADDED,
							System.currentTimeMillis());
					values.put(Audio.Media.DATE_MODIFIED,
							System.currentTimeMillis());
					values.put(Audio.Media.DISPLAY_NAME, title);
					values.put(Audio.Media.IS_MUSIC, 1);

					/*
					 * Not really for duration, but for the start time in the
					 * cue
					 */
					long pos = positionToMiliSec(track.getIndices().get(0)
							.getPosition());
					values.put(Audio.Media.DURATION, pos);

					/* Not only path, but appended with the track index */
					values.put(Audio.Media.DATA, data + "/" + index);

					values.put(Audio.Media.TRACK, track.getNumber());
					values.put(Audio.Media.MIME_TYPE, MIME_EX_AUDIO_CUE_TRACK);
					int updated = getContentResolver().update(
							Audio.Media.EXTERNAL_CONTENT_URI, values,
							Audio.Media.DATA + "=?", new String[] { data });
					// Log.i(TAG, updated + " rows updated. (Cue)");
					if (updated <= 0) {
						Uri uri = getContentResolver().insert(
								Audio.Media.EXTERNAL_CONTENT_URI, values);
						// Log.i(TAG, "(Cue) Newly inserted, uri=" + uri);
					}
					++index;
				}
			} catch (Exception e) {
				Log.e(TAG, "", e);
			}
			return null;
		}

	}

	public long positionToMiliSec(Position p) {
		long minP = p.getMinutes() * 60000;
		long secP = p.getSeconds() * 1000;
		long frmP = p.getFrames() * 13;
		return minP + secP + frmP;
	}

	private static final String CLEAN_SELECTION = Audio.Media.MIME_TYPE
			+ " LIKE " + "?";
	private static final String[] CLEAN_SELECTION_ARGS = new String[] { MIME_EX_AUDIO
			+ "%" };

	class CleanTask extends AsyncTask<Void, Void, Void> {

		@Override
		protected Void doInBackground(Void... params) {
			Cursor c = getContentResolver().query(
					Audio.Media.EXTERNAL_CONTENT_URI,
					new String[] { Audio.Media.DATA, Audio.Media.MIME_TYPE,
							Audio.Media._ID }, CLEAN_SELECTION,
					CLEAN_SELECTION_ARGS, null);
			if (c == null) {
				return null;
			}
			while (c.moveToNext()) {
				String path = c.getString(0);
				String mime = c.getString(1);
				if (mime.equals(MIME_EX_AUDIO_CUE_TRACK)) {
					int lastslash = path.lastIndexOf('/');
					if (lastslash > 0) {
						path = path.substring(0, lastslash);
					}
				}
				File f = new File(path);
				if (!f.exists()) {
					long id = c.getLong(2);
					int row = getContentResolver().delete(
							ContentUris.withAppendedId(
									Audio.Media.EXTERNAL_CONTENT_URI, id),
							null, null);
					// Log.i(TAG, row + " rows deleted.");
				}
			}
			c.close();
			return null;
		}
	}
}
